﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceModel;
using System.Web;
using SecurityClient;

namespace SecurityModule.Workflows
{
    class WFPostHomeResetPassword : SimpleWorkflow
    {
        public WFPostHomeResetPassword()
        {
            Verb = "POST";
            Path = "/Home/ResetPassword";
        }
        public override void PreProcessImplementation(HttpContext context)
        {
            // verify token, store details of the account pending approval and add Email and ClientCode to Request.Form
            var callback = new Func<string, string>(content =>
                {
                    //Read the form fields and white list them.
                    var fields = HttpUtility.ParseQueryString(content);

                    //Read fields
                    var tokenId = fields["ResetToken"];
                    var password = fields["NewPassword"];
                    var password2 = fields["NewPassword2"];
                    var yubikeyOTP = fields["YubikeyOTP"];

                    //Whitelist the following fields to prevent password from being sidebanded.
                    string[] whitelistFields = { "ResetToken", "Email" };
                    foreach (var key in fields.AllKeys.Where(key => !whitelistFields.Contains(key)))
                    {
                        fields.Remove(key);
                    }

                    var hasErrors = false;
                    if (string.IsNullOrEmpty(tokenId))
                    {
                        fields.Add("EXTERRMSG", "Missing verification token");
                        hasErrors = true;
                    }
                    if (string.IsNullOrEmpty(password))
                    {
                        fields.Add("EXTERR_NewPassword", "Please provide a password");
                        hasErrors = true;
                    }
                    if (string.IsNullOrEmpty(password2))
                    {
                        fields.Add("EXTERR_NewPassword2", "Please confirm your password");
                        hasErrors = true;
                    }
                    if (!hasErrors && !password.Equals(password2))
                    {
                        fields.Add("EXTERR_NewPassword2", "Passwords don't match");
                        hasErrors = true;
                    }
                    if (hasErrors)
                    {
                        return fields.ToString();
                    }

                    try
                    {
                        //Anon is attempting to confirm their identity
                        var security = new SecurityServiceProxy();
                        var email = security.GetTokenFieldValue(tokenId, "EmailAddress") as string;
                        if (string.IsNullOrEmpty(email))
                        {
                            // send up an error to the application
                            fields.Add("EXTERRMSG", "Invalid password reset token");
                        }
                        else // token is verified
                        {
                            fields.Add("Email", email);

                            // store a token for reset password (will be used when approved)
                            var result = security.RequestPasswordReset(email, password, tokenId, yubikeyOTP);

                            if (result.Success)
                            {
                                fields.Add("Token", result.TokenId);
                            }
                            else
                            {
                                //Bubble up an error to the application
                                fields.Add("EXTERRMSG", result.ErrorMessage);
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        if (exception is PipeException || exception is EndpointNotFoundException)
                        {
                            //If the security service is offline or broken
                            if (fields.AllKeys.Contains("EXTERRMSG"))
                            {
                                fields["EXTERRMSG"] = SecurityServiceDownMsg;
                            }
                            else
                            {
                                fields.Add("EXTERRMSG", SecurityServiceDownMsg);
                            }
                        }
                        else
                        {
                            if (fields.AllKeys.Contains("EXTERRMSG"))
                            {
                                fields["EXTERRMSG"] = exception.Message;
                            }
                            else
                            {
                                fields.Add("EXTERRMSG", exception.Message);
                            }
                        }
                    }

                    return fields.ToString();
                });
            context.Request.Filter = new RequestFilter(context.Request.Filter, context.Request.ContentEncoding, callback);

            context.Response.Filter = new ReplaceFilter(context.Response.Filter, context.Response.ContentEncoding,
                new Dictionary<string, string>
                    {
                        {
                            "{{YubikeyOTP}}",
                            @"<label class='control-label' for='YubikeyOTP'>Current Yubikey</label><input name='YubikeyOTP' type='text' class='form-control'>"
                        }
                    }
                );
        }

        public override void PostProcessImplementation(HttpContext context)
        {
            // No post-processing
        }
    }
}
